package njust.csc.aeapi.utils

import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.servlet.http.HttpServletRequest
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.context.request.RequestAttributes
import org.springframework.web.context.request.RequestContextHolder
import java.lang.reflect.Parameter
private val logger = KotlinLogging.logger {}

/**
 * @Author ZhouHang
 * @Date 24/6/14 周五
 */

/**
 * 使用 @LogRequest 来记录接口调用情况
 * */
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogRequest

/**
 * 每次请求需要记录的信息数据类
 * @property signature String 调用了 Controller 中哪个函数的签名
 * @property url String 请求完整 url
 * @property uri String url 除去域名的部分
 * @property remoteIp String 远程 IP 地址（Deprecated）
 * @property queryString String GET请求的参数列表
 * @property method String 请求方式（GET/POST）
 * @property spendTime Int 接口响应时间/ms
 * @property result String 接口返回结果
 * @property parameters String 接口参数信息
 * @constructor
 */
@Serializable
data class WebLog (
    val signature: String = "",
    val url: String = "",
    val uri: String = "",
    val remoteIp: String = "",
    val queryString: String = "",
    val method: String = "",
    val spendTime: Int = 0,
    val result: String = "",
    val parameters: String = ""
)

@Aspect
@Component
class LogAspect {
    @Around("@annotation(njust.csc.aeapi.utils.LogRequest)")
    fun logRequest(joinPoint: ProceedingJoinPoint): Any {
        val requestAttributes = RequestContextHolder.getRequestAttributes()
        val request = requestAttributes!!.resolveReference(RequestAttributes.REFERENCE_REQUEST) as HttpServletRequest?

        val start = System.currentTimeMillis()
        val result = joinPoint.proceed()
        val end = System.currentTimeMillis()

        val signature = joinPoint.signature as MethodSignature
        val webLog = WebLog(
            method = request?.method.toString(),
            signature = signature.toShortString(),
            url = request?.requestURL.toString(),
            uri = request?.requestURI.toString(),
            remoteIp = request?.remoteUser.toString(),
            queryString = request?.queryString.toString(),
            spendTime = (end - start).toInt(),
            // 如果是 post 请求，日志记录返回结果
            result = if (request?.method.equals("POST")) result.toString() else "",
            parameters = getParameter(signature.method.parameters, joinPoint.args)
        )
        logger.info { Json.encodeToString(webLog) }
        return result
    }

    private fun getParameter(parameters: Array<Parameter>, arguments: Array<Any>): String {
        val args = mutableListOf<String>()
        for (i in parameters.indices) {
            // 将 RequestBody 注解修饰的参数视为请求参数
            val requestBody = parameters[i].getAnnotation(RequestBody::class.java)
            if (requestBody != null) {
                args.add(arguments[i].toString())
            }
        }
        return when (args.size) {
            0 -> ""
            1 -> args[0]
            else -> args.toString()
        }
    }
}